home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-21
/
xcb-20.zip
/
XCB.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-11-11
|
23KB
|
917 lines
/*
* xcb: Copyright (C) 1992 by Farrell McKay.
* XView modifications provided by Danny Vanderryn.
*
* Simple X interface to the cut buffers in an X server.
* The program creates a window subdivided into a number of subwindows,
* one per cut buffer. The user may copy cut buffer contents around
* using mouse buttons 1 and 2, or rotate the buffers using mouse
* button 3. Buffers may be cleared by using Shift-button 2.
*
* Note that this program assumes the cut buffers contain textual
* information, and displays buffer contents using the XDrawString
* function. It is not suitable for use in any other environment.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, provided
* that the above copyright notice appears in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. This software is provided "as is" without express or
* implied warranty.
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h> /* for strcmp() */
#ifdef __STDC__
#include <stdlib.h> /* for exit()... */
#endif
#include <unistd.h> /* for read(), write() */
#include <X11/Xatom.h> /* for pre-defined atom names */
#include <X11/StringDefs.h> /* for XtNforeground et.al.*/
#include <X11/Intrinsic.h>
#include <X11/Xaw/Form.h>
#include "cb.h"
#ifdef MSDOS
#include "patchlev.h" /* KSW 11/10/1992 4:07pm. */
#else
#include "patchlevel.h"
#endif /* MSDOS */
#define _printf (void) printf
#define _fprintf (void) fprintf
#define _sprintf (void) sprintf
#define eq(a,b) (strcmp((a),(b)) == 0)
#define min(a,b) ((a) < (b)? (a): (b))
#define max(a,b) ((a) > (b)? (a): (b))
#define XtNbufferCount "bufferCount" /* Application resources */
#define XtCBufferCount "BufferCount"
#define XtNlayout "layout"
#define XtCLayout "Layout"
#define PGM_NAME "xcb"
#define PGM_CLASS "Xcb"
#define BUFINC 2048
typedef unsigned char uchar;
static Display *dpy;
static Window root;
static XtAppContext app;
static Widget top, box, *wdg;
static Atom *atom;
static int natoms, nbuffs;
static int argc;
static char **argv;
#ifdef XVIEW
static Atom caret, clipboard, yield;
static Atom length, lengthc;
static Atom text, ctext;
#endif
/*
* Fetch the contents of cut buffer n from the root window.
*/
static char *
fetch_buffer(a, nb)
Atom a;
int *nb;
{
Atom actl_type;
int actl_format;
unsigned long nitems, after;
char *data;
*nb = 0;
if (XGetWindowProperty(dpy, root, a,
0L, 10000000L, False, XA_STRING,
&actl_type, &actl_format,
&nitems, &after, (uchar **) &data) != Success)
return NULL;
if (actl_type == XA_STRING && actl_format != 32) {
*nb = nitems;
return data;
}
if (data != NULL)
XFree(data);
return NULL;
}
/*
* Store the string p into cut buffer n on the root window.
*/
static void
store_buffer(p, nb, atom)
char *p;
int nb;
Atom atom;
{
XChangeProperty(dpy, root, atom, XA_STRING,
8, PropModeReplace, (uchar *) p, nb);
}
/*
* Add an atom to the program's atom cache.
*/
static Atom
get_atom(n, ifexists)
int n, ifexists;
{
char tmp[32];
if (n >= natoms) {
atom = (Atom *) XtRealloc((char *) atom, (n+1) * sizeof(Atom));
while (natoms < n+1)
atom[natoms++] = 0;
}
if (!atom[n]) {
_sprintf(tmp, "CUT_BUFFER%d", n);
atom[n] = XInternAtom(dpy, tmp, (Bool) ifexists);
}
return atom[n];
}
/*
* Draw a string in the window with top-left corner justification.
*/
static void
place_text(cb, str, len, y)
CbWidget cb;
char *str;
int len, y;
{
int cols;
GC gc;
gc = (cb->hilite)? cb->gc_inv: cb->gc;
if (y <= (int) cb->core.height) {
cols = ((int) cb->core.width + cb->font_height - 1)
/ cb->font_width;
len = min(len, cols);
if (len > 0) {
y += cb->font->ascent;
XDrawImageString(dpy, cb->core.window, gc,
0, y, str, len);
}
}
}
/*
* ============================================================================
* The following collection of functions and data structures define
* the cb widget. Each cb widget displays a single cut buffer value
* in a window, and provides cut and paste access to that buffer.
*/
static void
cb_initialize(req, wdg, args, nargs) /*ARGSUSED*/
Widget req, wdg;
ArgList args;
Cardinal *nargs;
{
CbWidget cb = (CbWidget) wdg;
cb->font_width = cb->font->max_bounds.width;
cb->font_height = cb->font->ascent + cb->font->descent;
cb->gc = 0;
cb->gc_inv = 0;
cb->hilite = 0;
XChangeProperty(dpy, root, cb->atom, XA_STRING,
8, PropModeAppend, (uchar *) "", 0);
}
static void
cb_realize(wdg, mask, attrs)
Widget wdg;
XtValueMask *mask;
XSetWindowAttributes *attrs;
{
CbWidget cb = (CbWidget) wdg;
XtGCMask v_mask = 0L;
XGCValues values;
XtCreateWindow(wdg, InputOutput, CopyFromParent, *mask, attrs);
values.font = cb->font->fid;
values.foreground = cb->fgnd;
values.background = cb->core.background_pixel;
v_mask = GCFont | GCForeground | GCBackground;
cb->gc = XtGetGC(wdg, v_mask, &values);
values.foreground = cb->core.background_pixel;
values.background = cb->fgnd;
cb->gc_inv = XtGetGC(wdg, v_mask, &values);
}
/*
* Redraw the contents of one of the subwindows.
* The function assumes the cut buffer contains text data, and parses
* it accordingly. The data is split into lines at each '\n' character.
* Lines which extend beyond the subwindow's borders are clipped; no
* wrap-around processing is done.
* Keep it simple.
*/
static void
cb_redisplay(wdg, event, region) /*ARGSUSED*/
Widget wdg;
XEvent *event;
Region region;
{
CbWidget cb = (CbWidget) wdg;
char *p, *pp, *base;
int y, nbytes;
y = 0;
p = pp = base = fetch_buffer(cb->atom, &nbytes);
while (pp < base + nbytes) {
if (*pp == '\n') {
place_text(cb, p, pp - p, y);
p = pp + 1;
y += cb->font_height;
}
pp++;
}
place_text(cb, p, pp - p, y);
XFree(base);
}
static void
cb_destroy(wdg)
Widget wdg;
{
CbWidget cb = (CbWidget) wdg;
XtReleaseGC(wdg, cb->gc);
XtReleaseGC(wdg, cb->gc_inv);
}
/*
* Make this widget the owner of the PRIMARY selection.
* The window contents are then redrawn with highlighting.
* It seems that if a XSetSelectionOwner is performed on a client's
* window, no SelectionClear event is generated if another window
* within the same client is already the selection owner.
* To force generation of the SelectionClear event, this function
* sets the selection ownership to None before taking it itself.
*/
static void
cb_cut(wdg, event, parms, nparms) /*ARGSUSED*/
Widget wdg;
XEvent *event;
String *parms;
Cardinal *nparms;
{
CbWidget cb = (CbWidget) wdg;
Window win = cb->core.window;
if (cb->hilite == 1)
return;
XSetSelectionOwner(dpy, XA_PRIMARY, None, event->xbutton.time);
XSetSelectionOwner(dpy, XA_PRIMARY, win, event->xbutton.time);
if (XGetSelectionOwner(dpy, XA_PRIMARY) == win) {
cb->hilite = 1;
XClearArea(dpy, win, 0,0,0,0, False);
cb_redisplay(wdg, (XEvent *)0, (Region)0);
#ifdef XVIEW
XSetSelectionOwner(dpy, caret, win, event->xbutton.time);
XSetSelectionOwner(dpy, clipboard, win, event->xbutton.time);
#endif
}
}
static void
cb_paste(wdg, event, parms, nparms) /*ARGSUSED*/
Widget wdg;
XEvent *event;
String *parms;
Cardinal *nparms;
{
CbWidget cb = (CbWidget) wdg;
Window w;
char *ptr;
int n;
w = XGetSelectionOwner(dpy, XA_PRIMARY);
if (w == None) {
ptr = fetch_buffer(atom[0], &n); /* copy from cb0 */
store_buffer(ptr, n, cb->atom);
XFree(ptr);
}
else if (w != cb->core.window)
XConvertSelection(dpy, XA_PRIMARY, XA_STRING,
cb->atom, root, event->xbutton.time);
}
static void
cb_clear(wdg, event, parms, nparms) /*ARGSUSED*/
Widget wdg;
XEvent *event;
String *parms;
Cardinal *nparms;
{
CbWidget cb = (CbWidget) wdg;
store_buffer("", 0, cb->atom);
if (cb->hilite) {
cb->hilite = 0;
XSetSelectionOwner(dpy, XA_PRIMARY, None, event->xbutton.time);
}
}
static void
cb_rotate(wdg, event, parms, nparms) /*ARGSUSED*/
Widget wdg;
XEvent *event;
String *parms;
Cardinal *nparms;
{
int n = 0;
if (*nparms > 0)
n = atoi(parms[0]);
if (n != 0)
XRotateWindowProperties(dpy, root, atom, natoms, n);
}
static void
cb_quit(wdg, event, parms, nparms) /*ARGSUSED*/
Widget wdg;
XEvent *event;
String *parms;
Cardinal *nparms;
{
exit(0);
}
/*
* Clear and redraw the widget's window.
*/
static void
cb_refresh(wdg, event, parms, nparms) /*ARGSUSED*/
Widget wdg;
XEvent *event;
String *parms;
Cardinal *nparms;
{
XClearArea(dpy, wdg->core.window, 0,0,0,0, False);
cb_redisplay(wdg, (XEvent *)0, (Region)0);
}
/*
* Someone or something wants a copy of the current PRIMARY selection.
* Such a request is only satisfied if the target type is STRING.
* (No conversion facilities are provided by this program).
* The selection request is met by copying the current contents
* of the cut buffer to the target window+atom.
*/
static void
cb_selreq(wdg, event, parms, nparms) /*ARGSUSED*/
Widget wdg;
XEvent *event;
String *parms;
Cardinal *nparms;
{
int nbytes;
char *ptr;
XSelectionEvent notify;
XSelectionRequestEvent *rq;
CbWidget cb = (CbWidget) wdg;
#ifdef XVIEW
unsigned long data;
#endif
rq = (XSelectionRequestEvent *) event;
notify.type = SelectionNotify;
notify.display = rq->display;
notify.requestor = rq->requestor;
notify.selection = rq->selection;
notify.target = rq->target;
notify.property = None;
notify.time = rq->time;
#ifdef XVIEW
if (rq->selection == XA_PRIMARY) {
if (rq->target == yield) {
/* tell 'em we'll give it up */
data = 1;
XChangeProperty(dpy, rq->requestor, rq->property,
rq->target, 32, PropModeReplace,
(uchar *) &data, 1);
notify.property = rq->property;
}
else {
#endif
if (cb->hilite && rq->target == XA_STRING) {
ptr = fetch_buffer(cb->atom, &nbytes);
XChangeProperty(dpy, rq->requestor, rq->property, XA_STRING,
8, PropModeReplace, (uchar *) ptr, nbytes);
notify.property = rq->property;
XFree(ptr);
}
#ifdef XVIEW
}
}
else if (rq->selection == caret) {
if (rq->target == yield) {
/*
* Give up the caret (which meant that we
* own the clipboard)
*/
XSetSelectionOwner(dpy, caret, None,
event->xselectionrequest.time);
data = 1;
XChangeProperty(dpy, rq->requestor, rq->property,
rq->target, 32, PropModeReplace,
(uchar *) &data, 1);
notify.property = rq->property;
}
}
else if (rq->selection == clipboard && cb->hilite) {
ptr = fetch_buffer(cb->atom, &nbytes);
if (rq->target == lengthc || rq->target == length) {
/*
* Send the length of the selection.
*/
data = nbytes;
XChangeProperty(dpy, rq->requestor, rq->property,
rq->target, 32, PropModeReplace,
(uchar *) &data, 1);
notify.property = rq->property;
}
else if (rq->target == XA_STRING ||
rq->target == text || rq->target == ctext) {
/*
* Send the selection itself.
* All of our selections will be XA_STRING,
* but if they ask for COMPOUND_TEXT, it's ok
* to say that that's what we've got...
*/
XChangeProperty(dpy, rq->requestor, rq->property,
(rq->target == ctext? ctext: XA_STRING), 8,
PropModeReplace, ptr, nbytes);
notify.property = rq->property;
}
XFree(ptr);
}
#endif
XSendEvent(dpy, rq->requestor, False, 0, (XEvent *) ¬ify);
}
/*
* Boo hiss, someone has taken the PRIMARY selection ownership
* away from this widget. The current window contents must
* be redrawn without highlighting.
*/
static void
cb_selclear(wdg, event, parms, nparms) /*ARGSUSED*/
Widget wdg;
XEvent *event;
String *parms;
Cardinal *nparms;
{
CbWidget cb = (CbWidget) wdg;
#ifdef XVIEW
Window w;
Time t;
#endif
cb->hilite = 0;
XClearArea(dpy, cb->core.window, 0,0,0,0, False);
cb_redisplay(wdg, (XEvent *)0, (Region)0);
#ifdef XVIEW
/*
* Since we don't have ownership of PRIMARY anymore,
* we'd better get rid of CLIPBOARD and _SUN_SELN_CARET,
* if they're still ours.
*/
t = event->xselectionclear.time;
w = XGetSelectionOwner(dpy, caret);
if (w == cb->core.window)
XSetSelectionOwner(dpy, caret, None, t);
w = XGetSelectionOwner(dpy, clipboard);
if (w == cb->core.window)
XSetSelectionOwner(dpy, clipboard, None, t);
#endif
}
static XtResource resources[] = {
#define offset(field) XtOffset(CbWidget, field)
/* {name, class, type, size, offset, default_type, default_addr}, */
{ XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
offset(fgnd), XtRString, (XtPointer) "XtDefaultForeground"},
{ XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
offset(font), XtRString, (XtPointer) "fixed" },
{ XtNatom, XtCAtom, XtRAtom, sizeof(Atom), /* internal use */
offset(atom), XtRImmediate, (XtPointer)0 },
#undef offset
};
static XtActionsRec actions[] = {
{ "cut", cb_cut },
{ "paste", cb_paste },
{ "clear", cb_clear },
{ "rotate", cb_rotate },
{ "quit", cb_quit },
{ "refresh", cb_refresh },
{ "selreq", cb_selreq },
{ "selclear", cb_selclear },
};
static char cb_transl[] = "\
<Btn1Down>: cut() \n\
Shift <Btn2Down>: clear() \n\
<Btn2Down>: paste() \n\
Shift <Btn3Down>: rotate(-1) \n\
<Btn3Down>: rotate(1) \n\
<Key>Left: rotate(-1) \n\
<Key>Right: rotate(1) \n\
<Key>Up: rotate(-1) \n\
<Key>Down: rotate(1) \n\
<Key>q: quit() \n\
<SelReq>: selreq() \n\
<SelClr>: selclear() \n\
";
CbClassRec cbClassRec = {
{
(WidgetClass) &widgetClassRec, /* superclass */
"Buffer", /* class_name */
sizeof(CbRec), /* widget_size */
NULL, /* class_initialize */
NULL, /* class_part_initialize */
FALSE, /* class_inited */
cb_initialize, /* initialize */
NULL, /* initialize_hook */
cb_realize, /* realize */
actions, /* actions */
XtNumber(actions), /* num_actions */
resources, /* resources */
XtNumber(resources), /* num_resources */
NULLQUARK, /* xrm_class */
TRUE, /* compress_motion */
TRUE, /* compress_exposure */
TRUE, /* compress_enterleave */
FALSE, /* visible_interest */
cb_destroy, /* destroy */
NULL, /* resize */
cb_redisplay, /* expose */
NULL, /* set_values */
NULL, /* set_values_hook */
XtInheritSetValuesAlmost, /* set_values_almost */
NULL, /* get_values_hook */
NULL, /* accept_focus */
XtVersion, /* version */
NULL, /* callback_private */
cb_transl, /* tm_table */
XtInheritQueryGeometry, /* query_geometry */
XtInheritDisplayAccelerator, /* display_accelerator */
NULL /* extension */
},
};
WidgetClass cbWidgetClass = (WidgetClass) &cbClassRec;
/*
* Here endeth the section concerned with the cb widget.
* Normal viewing shall now be resumed.
* ============================================================================
*/
static void
usage()
{
_fprintf(stderr,
"Usage: %s [Xt option] [-l layout] [-n count] [-p|-s list] [-r count]\n",
PGM_NAME);
exit(1);
}
/*
* Gracefully exit after an XIO error.
* This avoids messy error messages sometimes seen from xterm
* or in the xdm-errors file when forcibly destroying the client program.
*/
static int
xioerror(d) /*ARGSUSED*/
Display *d;
{
exit(1); /*NOTREACHED*/
}
/*
* Print the contents of a cut buffer on stdout.
*/
static void
doprint(n, ptr, nb)
int n;
char *ptr;
int nb;
{
Atom a;
a = get_atom(n, True);
if (a) {
ptr = fetch_buffer(a, &nb);
if (write(1, ptr, nb) != nb) {
_fprintf(stderr, "Write error\n");
exit(1);
}
XFree(ptr);
}
}
/*
* Load a new value into one of the cut buffers.
*/
static void
doset(n, ptr, nb)
int n;
char *ptr;
int nb;
{
store_buffer(ptr, nb, get_atom(n, False));
}
/*
* Process an ASCII list of cut buffer numbers.
* Lists must obey the form "buffno[,buffno...]"
* where buffno is a non-negative integer or a range
* of the form M-N. A processing function is called
* for each buffer number in the list. Duplicates and
* list ordering is significant.
*/
static void
dolist(list, fn, data, nbytes)
char *list;
void (*fn)();
char *data;
int nbytes;
{
int m, n, x;
while (*list) {
if (!isdigit(*list))
usage();
for (m = 0; isdigit(*list); list++)
m = m * 10 + *list - '0';
(*fn)(m, data, nbytes);
if (*list == '-') {
list++;
if (!isdigit(*list))
usage();
for (n = 0; isdigit(*list); list++)
n = n * 10 + *list - '0';
x = (m > n)? -1: 1;
while (m != n) {
m += x;
(*fn)(m, data, nbytes);
}
}
if (*list == ',')
list++;
else if (*list)
usage();
}
}
/*
* Perform a task mode command, i.e.
* do something to the cut buffers immediately,
* without the need to create any X windows first.
*/
static void
dotask(cmd, arg)
int cmd;
char *arg;
{
char *ptr;
int i, n, nb;
ptr = (char *)0;
n = nb = 0;
switch (cmd) {
case 'p': /* print one or more buffers */
dolist(arg, doprint, (char *)0, 0);
break;
case 'r': /* rotate the buffer contents */
n = atoi(arg);
if (n == 0)
break;
for (i = nbuffs - 1; i >= 0; i--)
(void) get_atom(i, False);
for (i = nbuffs - 1; i >= 0; i--)
XChangeProperty(dpy, root, atom[i], XA_STRING,
8, PropModeAppend, (uchar *) "", 0);
XRotateWindowProperties(dpy, root, atom, nbuffs, n);
break;
case 's': /* store data in one or more buffers */
do {
ptr = XtRealloc(ptr, nb + BUFINC);
i = BUFINC;
do {
n = read(0, ptr + nb, i);
nb += n;
i -= n;
} while (n > 0 && i > 0);
} while (n > 0);
if (n == -1) {
_fprintf(stderr, "Read error\n");
exit(1);
}
dolist(arg, doset, ptr, nb);
XtFree(ptr);
break;
}
}
typedef struct {
int nbuffs;
char *layout;
} ares_t, *ares_ptr;
static ares_t ares;
static XtResource res[] = {
#define offset(field) XtOffset(ares_ptr, field)
{ XtNbufferCount, XtCBufferCount, XtRInt, sizeof(int),
offset(nbuffs), XtRImmediate, (XtPointer) 8 },
{ XtNlayout, XtCLayout, XtRString, sizeof(char *),
offset(layout), XtRImmediate, "horiz" },
#undef offset
};
static char *def[] = { /* default resource values */
".bufferCount: 8",
".layout: horizontal",
"*font: fixed",
"*Buffer.width: 60",
"*Buffer.height: 60",
0,
};
static XrmOptionDescRec opt[] = {
{ "-n", ".bufferCount", XrmoptionSepArg, (caddr_t) 8 },
{ "-l", ".layout", XrmoptionSepArg, (caddr_t) "horiz" },
};
/*
* Parse the command line options, and
* perform all the windows initializations.
*/
static void
init()
{
int i, n;
char **p;
char *attach = 0;
char name[16];
Arg args[2];
/*
* Set up the atoms that we already know about.
*/
natoms = 8;
atom = (Atom *) XtMalloc(natoms * sizeof(Atom));
atom[0] = XA_CUT_BUFFER0;
atom[1] = XA_CUT_BUFFER1;
atom[2] = XA_CUT_BUFFER2;
atom[3] = XA_CUT_BUFFER3;
atom[4] = XA_CUT_BUFFER4;
atom[5] = XA_CUT_BUFFER5;
atom[6] = XA_CUT_BUFFER6;
atom[7] = XA_CUT_BUFFER7;
/*
* Initialize the toolkit, parse the command line,
* initialize the resources database, and find out
* how many buffers to deal with.
*/
top = XtAppInitialize(&app, PGM_CLASS, opt, 2, &argc, argv, def, 0, 0);
dpy = XtDisplay(top);
root = RootWindow(dpy, DefaultScreen(dpy));
XtGetApplicationResources(top, &ares, res, XtNumber(res), 0, 0);
nbuffs = max(ares.nbuffs, 1);
/*
* If the command line contains one of the task mode
* switches (print, set, rotate), it is processed here.
* If there are multiple task mode args, only the first
* one is processed.
*/
for (p = argv + 1; p < argv + argc; p++) {
if (eq(*p, "-p") || eq(*p, "-r") || eq(*p, "-s")) {
if (p == argv + argc - 1)
usage();
dotask(p[0][1], p[1]);
XCloseDisplay(dpy);
exit(0);
}
}
/*
* If no task mode request has been made of us, this code
* proceeds with the rest of the windows initialization.
* The container widget is created, the sub-widgets are
* created and then everything is realized.
*/
if (argc > 1)
usage();
if (ares.layout[0] == 'h')
attach = XtNfromHoriz;
if (ares.layout[0] == 'v')
attach = XtNfromVert;
wdg = (Widget *) XtMalloc(nbuffs * sizeof(Widget));
box = XtCreateWidget("container", formWidgetClass, top, 0, 0);
XtManageChild(box);
for (i = 0; i < nbuffs; i++) {
XtSetArg(args[0], XtNatom, get_atom(i, False));
n = 1;
if (attach && i > 0) {
XtSetArg(args[1], attach, wdg[i-1]);
n = 2;
}
_sprintf(name, "buffer%d", i);
wdg[i] = XtCreateWidget(name, cbWidgetClass, box, args, n);
XtManageChild(wdg[i]);
}
XSelectInput(dpy, root, PropertyChangeMask);
XSetIOErrorHandler(xioerror);
XtRealizeWidget(top);
#ifdef XVIEW
clipboard = XInternAtom(dpy, "CLIPBOARD", False);
caret = XInternAtom(dpy, "_SUN_SELN_CARET", False);
yield = XInternAtom(dpy, "_SUN_SELN_YIELD", False);
length = XInternAtom(dpy, "LENGTH", False);
lengthc = XInternAtom(dpy, "LENGTH_CHARS", False);
text = XInternAtom(dpy, "TEXT", False);
ctext = XInternAtom(dpy, "COMPOUND_TEXT", False);
#endif
}
/*
* Process incoming events.
* The program needs to know when a cut buffer value changes.
* We achieve this by eavesdropping on PropertyNotify events arising
* on the root window. If such an event is delivered to us, the
* function immediately clears the window displaying that property
* and causes an Expose event to be generated for the window.
* This is horrible nasty stuff.
*/
static void
xevents()
{
XEvent event;
int i;
for (;;) {
XtAppNextEvent(app, &event);
if (event.type != PropertyNotify) {
(void) XtDispatchEvent(&event);
continue;
}
for (i = 0; i < natoms; i++)
if (event.xproperty.atom == atom[i])
XClearArea(dpy, XtWindow(wdg[i]), 0,0,0,0,True);
}
}
main(_argc, _argv)
int _argc;
char **_argv;
{
argc = _argc;
argv = _argv;
init();
xevents();
return 0;
}